package com.qsw.fileoperatelib;

import com.qsw.fileoperatelib.bean.TwoDimenArray;
import com.qsw.fileoperatelib.bsdiff.PatchUtil;
import com.qsw.fileoperatelib.encode.EncodeUtil;
import com.qsw.fileoperatelib.excel.ExcelUtil;
import com.qsw.fileoperatelib.excel.bean.impl.IExportExcel;
import com.qsw.fileoperatelib.iedata.BIEDataUtil;
import com.qsw.fileoperatelib.iedata.IEDataInEndUtil;
import com.qsw.fileoperatelib.zip.ZipUtil;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

import jxl.read.biff.BiffException;
import jxl.write.WriteException;

/**
 * 文件操作综合管理类
 */
public class FileOperateUtil {

    /**************************** Excel工具 **********************************/

    /**
     * 将指定的List写入Excel中
     *
     * @param dataList         待写入的list
     * @param parentFolderPath 根文件夹所在目录
     * @param timestamp        用于创建文件夹及文件的时间标记
     * @return 是否成功写入
     */
    public static boolean writeToExcel(List<? extends IExportExcel> dataList, String parentFolderPath, long timestamp) throws WriteException, IOException {
        return ExcelUtil.writeToExcel(dataList, parentFolderPath, timestamp);
    }

    /**
     * 将指定的List写入Excel中
     *
     * @param dataList         待写入的list
     * @param parentFolderPath 根文件夹所在目录
     * @param timestamp        用于创建文件夹及文件的时间标记
     * @param signName         文件命名特征
     * @return 是否成功写入
     */
    public static boolean writeToExcel(List<? extends IExportExcel> dataList, String parentFolderPath, long timestamp, String signName) throws WriteException, IOException {
        return ExcelUtil.writeToExcel(dataList, parentFolderPath, timestamp, signName);
    }

    /**
     * 将指定的List写入Excel中
     *
     * @param dataList      待写入的list
     * @param fileWholePath 文件完整路径
     * @param sheetName     Excel工作表的表名
     * @return 是否成功写入
     */
    public static boolean writeToExcel(List<? extends IExportExcel> dataList, String fileWholePath, String sheetName) throws WriteException, IOException {
        return ExcelUtil.writeToExcel(dataList, fileWholePath, sheetName);
    }

    /**
     * 读取Excel
     *
     * @param excelFile excel文件
     * @param cls       要承载的IExportExcel子类
     * @param <E>       要承载的IExportExcel子类类型
     * @return 结果
     * @throws BiffException
     * @throws IOException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <E extends IExportExcel> List<E> readFromExcel(File excelFile, Class<E> cls) throws BiffException, IOException, IllegalAccessException, InstantiationException {
        return ExcelUtil.readFromExcel(excelFile, cls);
    }

    /**
     * 读取Excel
     *
     * @param excelFile excel文件
     */
    public static TwoDimenArray<String> readFromExcel(File excelFile) throws IOException, BiffException {
        return ExcelUtil.readFromExcel(excelFile);
    }

    /**************************** 压缩工具 **********************************/

    /**
     * 压缩文件和文件夹
     *
     * @param srcFilePath    要压缩的文件或文件夹路径
     * @param dstZipFilePath 压缩完成的Zip文件路径
     * @param deleteIfExits  true：若目标压缩文件存在则删除重建；false：若目标压缩文件存在则返回不操作
     * @throws IOException 文件操作异常
     */
    public static void zip(String srcFilePath, String dstZipFilePath, boolean deleteIfExits) throws IOException {
        ZipUtil.zip(srcFilePath, dstZipFilePath, deleteIfExits);
    }

    /**
     * 解压文件
     *
     * @param zipFile   zip文件路径
     * @param targetDir 解压到的目标位置
     * @throws IOException 文件操作异常
     */
    public static void unzip(String zipFile, String targetDir) throws IOException {
        ZipUtil.unzip(zipFile, targetDir);
    }

    /**************************** 补丁工具 **********************************/

    /**
     * 合并文件
     *
     * @param oldFile   旧文件路径
     * @param newFile   新文件路径（存储生成新文件的路径）
     * @param patchFile 差异文件
     */
    public static void patchFile(String oldFile, String newFile, String patchFile) {
        PatchUtil.patch(oldFile, newFile, patchFile);
    }

    /**************************** 数据注入与提取工具 注入内容位于文件末端 **********************************/

    private static BIEDataUtil mIEDataInEndUtil;


    /**
     * 向文件中注入信息
     *
     * @param file                     待注入信息的文件
     * @param dataList                 待注入的信息
     * @param onProgressChangeListener 进度回调
     * @return 注入信息后的文件，若为null则操作失败
     * @throws IOException              异常
     * @throws NoSuchAlgorithmException 异常
     */
    public static File injectEnd(File file, List<String> dataList, BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.inject(file, dataList, onProgressChangeListener);
    }

    /**
     * 向文件中注入信息
     *
     * @param file                     待注入信息的文件
     * @param dataList                 待注入的信息
     * @param newSuffix                注入后的文件后缀，若为null，则不重命名
     * @param onProgressChangeListener 进度回调
     * @return 注入信息后的文件，若为null则操作失败
     * @throws IOException              异常
     * @throws NoSuchAlgorithmException 异常
     */
    public static File injectEnd(File file, List<String> dataList, String newSuffix, BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.inject(file, dataList, newSuffix, onProgressChangeListener);
    }

    /**
     * 向文件中注入信息
     *
     * @param file                     待注入信息的文件
     * @param dataList                 待注入的信息
     * @param copyToFolder             不修改原文件，复制原文件并注入后保存在该目录下，若为null，则不复制直接修改原文件
     * @param newSuffix                注入后的文件后缀，若为null，则不重命名
     * @param deleteIfExist            true：若新文件存在则删除；false：若新文件存在则返回失败
     * @param onProgressChangeListener 进度回调
     * @return 注入信息后的文件，若为null则操作失败
     * @throws IOException              异常
     * @throws NoSuchAlgorithmException 异常
     */
    public static File injectEnd(File file, List<String> dataList, String copyToFolder, String newSuffix, boolean deleteIfExist,
                                 BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.inject(file, dataList, copyToFolder, newSuffix, deleteIfExist, onProgressChangeListener);
    }

    /**
     * 检查是否为已注入文件
     *
     * @param file 待检查的文件
     * @return true：已注入；false：未注入
     * @throws IOException 异常
     */
    public static boolean isInjectFileEnd(File file) throws IOException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.isInjectFile(file);
    }

    /**
     * 读取注入的数据并恢复注入文件为原始文件
     *
     * @param file                     待处理文件
     * @param checkMD5                 true：自动对恢复后的文件做MD5校验
     * @param onProgressChangeListener 进度回调
     * @return 读取到的数据，如果出错则返回null
     * @throws IOException 异常
     */
    public static BIEDataUtil.ReadDataAndRecoverResult readDataAndRecoverEnd(File file, boolean checkMD5,
                                                                             BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.readDataAndRecover(file, checkMD5, onProgressChangeListener);
    }

    /**
     * 读取注入的数据并恢复注入文件为原始文件
     *
     * @param file                     待处理文件
     * @param recoverSuffix            恢复后的文件后缀
     * @param checkMD5                 true：自动对恢复后的文件做MD5校验
     * @param onProgressChangeListener 进度回调
     * @return 读取到的数据，如果出错则返回null
     * @throws IOException 异常
     */
    public static BIEDataUtil.ReadDataAndRecoverResult readDataAndRecoverEnd(File file, String recoverSuffix,
                                                                             boolean checkMD5, BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.readDataAndRecover(file, recoverSuffix, checkMD5, onProgressChangeListener);
    }

    /**
     * 读取注入的数据并恢复注入文件为原始文件
     *
     * @param file                     待处理文件
     * @param copyToFolder             不修改原文件，复制原文件并注入后保存在该目录下，若为null，则不复制直接修改原文件
     * @param recoverSuffix            恢复后的文件后缀
     * @param deleteIfExist            true：若新文件存在则删除；false：若新文件存在则返回失败
     * @param checkMD5                 true：自动对恢复后的文件做MD5校验
     * @param onProgressChangeListener 进度回调
     * @return 读取到的数据，如果出错则返回null
     * @throws IOException 异常
     */
    public static BIEDataUtil.ReadDataAndRecoverResult readDataAndRecoverEnd(File file, String copyToFolder, String recoverSuffix, boolean deleteIfExist,
                                                                             boolean checkMD5, BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.readDataAndRecover(file, copyToFolder, recoverSuffix, deleteIfExist, checkMD5, onProgressChangeListener);
    }

    /**
     * 读取数据
     *
     * @param file                         文件
     * @param onSingleTypeProgressListener 进度
     * @return 读取到的数据，如果出错则返回null
     * @throws IOException 异常
     */
    public static BIEDataUtil.ReadDataResult readDataEnd(File file, BIEDataUtil.OnSingleTypeProgressListener onSingleTypeProgressListener) throws IOException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.readData(file, onSingleTypeProgressListener);
    }

    /**
     * 恢复注入文件为原始文件
     *
     * @param file                     待恢复文件
     * @param checkMD5Bytes            若为null则不做校验，否则将对恢复后的文件做MD5校验，若校验失败则认为操作失败
     * @param onProgressChangeListener 进度
     * @return 恢复后的文件，若为null则操作失败
     * @throws IOException 异常
     */
    public static File recoverFileEnd(File file, byte[] checkMD5Bytes, BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.recoverFile(file, checkMD5Bytes, onProgressChangeListener);
    }

    /**
     * 恢复注入文件为原始文件
     *
     * @param file                     待恢复文件
     * @param recoverSuffix            恢复后的文件后缀
     * @param checkMD5Bytes            若为null则不做校验，否则将对恢复后的文件做MD5校验，若校验失败则认为操作失败
     * @param onProgressChangeListener 进度
     * @return 恢复后的文件，若为null则操作失败
     * @throws IOException 异常
     */
    public static File recoverFileEnd(File file, String recoverSuffix, byte[] checkMD5Bytes, BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.recoverFile(file, recoverSuffix, checkMD5Bytes, onProgressChangeListener);
    }

    /**
     * 恢复注入文件为原始文件
     *
     * @param file                     待恢复文件
     * @param copyToFolder             不修改原文件，复制原文件并注入后保存在该目录下，若为null，则不复制直接修改原文件
     * @param recoverSuffix            恢复后的文件后缀
     * @param deleteIfExist            true：若新文件存在则删除；false：若新文件存在则返回失败
     * @param checkMD5Bytes            若为null则不做校验，否则将对恢复后的文件做MD5校验，若校验失败则认为操作失败
     * @param onProgressChangeListener 进度
     * @return 恢复后的文件，若为null则操作失败
     * @throws IOException 异常
     */
    public static File recoverFileEnd(File file, String copyToFolder, String recoverSuffix, boolean deleteIfExist,
                                      byte[] checkMD5Bytes, BIEDataUtil.OnProgressChangeListener onProgressChangeListener) throws IOException, NoSuchAlgorithmException {
        if (mIEDataInEndUtil == null) {
            mIEDataInEndUtil = IEDataInEndUtil.getInstance();
        }
        return mIEDataInEndUtil.recoverFile(file, copyToFolder, recoverSuffix, deleteIfExist, checkMD5Bytes, onProgressChangeListener);
    }


    /**************************** 加密/解密工具 **********************************/

    /**
     * 取反加密/取反解密
     * 对指定字节按位取反
     *
     * @param file      待处理文件
     * @param secretKey 取反位置，长度必须大于{@link EncodeUtil#REVERSAL_MIN_OK_COUNT}
     * @return true：成功；false：失败。至少{@link EncodeUtil#REVERSAL_MIN_OK_COUNT}个位置成功才认为最终成功
     * @throws IOException 异常
     */
    public static boolean encodeDecodeReversal(File file, long[] secretKey) throws IOException {
        return EncodeUtil.encodeDecodeReversal(file, secretKey);
    }
}
